package com.meshnetics.gb.stack.core.apl.type;

import com.meshnetics.gb.type.impl.zcl.FixedBytesDataValue;

/**
 * Endpoint address. Address use int value as u-byte, so it cannot be less than zero
 * or greater than 255. Address 255 is broadcast. Address 0 is zdo. Application objects
 * address range is 1..240. Address range 241..254 is reserved for future use.
 *
 * @zigbee.document ZIGBEE SPECIFICATION
 * @zigbee.version 053474r17
 * @zigbee.section 2.1.2, 2.2.5.1.2
 * @author Yury Aserchev (yaserchev@meshnetics.com)
 *         Date: 04.02.2008 13:09:48
 * @author mmarkov
 */
public final class EndPointAddress extends FixedBytesDataValue implements Comparable {
    /** Broadcast address value. */
    private static final int BROADCAST_ADDRESS = 0xFF;

    /** Max allowed application object address value. */
    private static final int MAX_ADDRESS = 0xF0;

    /** ZDO address value. */
    private static final int ZDO_ADDRESS = 0x00;

    /** Predefined constant - broadcast endpoint address. */
    public static final EndPointAddress BROADCAST = new EndPointAddress(BROADCAST_ADDRESS);

    /** Predefined constant - zdo endpoint address. */
    public static final EndPointAddress ZDO = new EndPointAddress(ZDO_ADDRESS);

    /**
     * Constructs new instance of address using passed address to init address value.
     * @param address address.
     * @throws IllegalArgumentException if address is out of range.
     */
    public EndPointAddress(int address) throws IllegalArgumentException {
        super(TypeEndPointAddress.TYPE, new byte[] { (byte) address });

        if ((address < 0) || (address > 0xFF) || isInvalid()) {
            throw new IllegalArgumentException("Invalid endpoint address: " + address);
        }
    }

    /**
     * Constructs EndPointAddress from the array of bytes (which should contain just 1 element).
     * It's needed for its constructing from TypeEndPointAddress.createValue() method.
     *
     * @param type TypeEndPointAddress
     * @param value byte array with 1 element
     */
    protected EndPointAddress(TypeEndPointAddress type, byte[] value) {
        super(type, value);

        if (isInvalid()) {
            throw new IllegalArgumentException("Invalid endpoint address: " + value[0]);
        }
    }

    /**
     * Returns true as EndPointAddress is effectively uint-8.
     *
     * @return true
     */
    protected boolean isUnsigned() {
        return true;
    }

    /**
     * Checks if the constructed EndPointAddress is withing allowed range.
     *
     * @return true if it is > 0xF0 but < 0xFF (i.e. invalid) and false otherwise
     */
    public boolean isInvalid() {
        int addr = getIntAddress();
        return (addr > MAX_ADDRESS && addr < BROADCAST_ADDRESS);
    }

    /**
     * Indicates if address is broadcast (address value is 255).
     * @return true if address is broadcast and false otherwise.
     */
    public boolean isBroadCast(){
        return getIntAddress() == BROADCAST_ADDRESS;
    }

    /**
     * Indicates if address is zdo (address value is 0).
     * @return true if address is zdo and false otherwise.
     */
    public boolean isZDO(){
        return getIntAddress() == ZDO_ADDRESS;
    }

    /**
     * Returns address value as int.
     * @return address value as int.
     */
    public int getIntAddress(){
        return (value[0] & 0xFF);
    }

    public int compareTo(final Object o) {
        final EndPointAddress that = (EndPointAddress) o;
        return getIntAddress() - that.getIntAddress();
    }
}
